home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 December / 2004-12 CHIP.iso / Internet / NVU 0.50 for Windows / nvu-0.50-win32-installer-full.exe / {app} / components / nsExtensionManager.js < prev    next >
Encoding:
JavaScript  |  2004-08-04  |  20.0 KB  |  659 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is the Extension Manager.
  16.  *
  17.  * The Initial Developer of the Original Code is Ben Goodger.
  18.  * Portions created by the Initial Developer are Copyright (C) 2004
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *  Ben Goodger <ben@bengoodger.com>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. var gExtensionManager = null;
  39.  
  40. function nsExtensionManager()
  41. {
  42.   var os = Components.classes["@mozilla.org/observer-service;1"]
  43.                      .getService(Components.interfaces.nsIObserverService);
  44.   os.addObserver(this, "profile-after-change", false);
  45.   
  46.   dump("*** global extensions startup!\n");
  47. }
  48.  
  49. nsExtensionManager.prototype = {
  50.  
  51.   // nsIObserver
  52.   observe: function (aSubject, aTopic, aData)
  53.   {
  54.     if (aTopic == "profile-after-change") {
  55.       var os = Components.classes["@mozilla.org/observer-service;1"]
  56.                         .getService(Components.interfaces.nsIObserverService);
  57.       os.removeObserver(this, "profile-after-change");
  58.       
  59.       dump("*** profile extensions startup\n");
  60.     }
  61.   },
  62.  
  63.   // nsIExtensionManager
  64.   installExtensionFromStream: function (aStream, aUseProfile)
  65.   {
  66.     var parser = Components.classes["@mozilla.org/rdf/xml-parser;1"]
  67.                            .createInstance(Components.interfaces.nsIRDFXMLParser);
  68.     var ds = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
  69.                        .createInstance(Components.interfaces.nsIRDFDataSource);
  70.     var streamListener = parser.parseAsync(ds, null);
  71.     
  72.     var bytesAvailable;
  73.     do {
  74.       bytesAvailable = aStream.available();
  75.       if (!bytesAvailable)
  76.         break;
  77.       
  78.       streamListener.onDataAvailable(null, null, aStream, 0, bytesAvailable);
  79.     }
  80.     while (1);
  81.     
  82.     this._ensureDS();
  83.     
  84.     this._ds.installExtension(ds, aUseProfile);
  85.   },
  86.       
  87.   uninstallExtension: function (aExtensionID)
  88.   {
  89.     this._ds.uninstallExtension(aExtensionID);
  90.   },
  91.   
  92.   enableExtension: function (aExtensionID)
  93.   {
  94.     this._ds.enableExtension(aExtensionID);
  95.   },
  96.   
  97.   disableExtension: function (aExtensionID)
  98.   {
  99.     this._ds.disableExtension(aExtensionID);
  100.   },
  101.   
  102.   updateExtension: function (aExtensionID, aDOMWindow)
  103.   {
  104.     var pref = Components.classes["@mozilla.org/preferences-service;1"]
  105.                          .getService(Components.interfaces.nsIPrefBranch);
  106.     var appID = pref.getCharPref(PREF_EM_APP_ID);
  107.     var appVersion = pref.getCharPref(PREF_EM_APP_VERSION);
  108.  
  109.     var ds = this._ds;
  110.     var extensionVersion = ds.getExtensionProperty(aExtensionID, "version");
  111.  
  112.     var itemCount = 0;
  113.     
  114.     var trigger = aDOMWindow.InstallTrigger;
  115.     
  116.     function doneUpdatingExtension(aExtensionID, aXPIURL)
  117.     {
  118.       var name = ds.getExtensionProperty(aExtensionID, "name");
  119.       var obj = {};
  120.       obj[name] = aXPIURL;
  121.       if (trigger.updateEnabled())
  122.         trigger.install(obj);
  123.     }
  124.     
  125.     var updater = new nsExtensionUpdater(aExtensionID, extensionVersion, appID, appVersion);
  126.     updater.checkForUpdates(doneUpdatingExtension);
  127.   },  
  128.   
  129.   // Themes
  130.   installTheme: function (aThemeID)
  131.   {
  132.   
  133.   },
  134.   
  135.   uninstallTheme: function (aThemeID)
  136.   {
  137.   
  138.   },
  139.  
  140.   updateTheme: function (aThemeID)
  141.   {
  142.   
  143.   },
  144.   
  145.   get datasource()
  146.   {
  147.     this._ensureDS();
  148.     return this._ds;
  149.   },
  150.   
  151.   //
  152.   _ds: null,
  153.     
  154.   // Other
  155.   _ensureDS: function ()
  156.   {
  157.     if (!this._ds) {
  158.       this._ds = new nsExtensionsDataSource();
  159.       if (this._ds) {
  160.         this._ds.loadExtensions(false);
  161.         this._ds.loadExtensions(true);
  162.       }
  163.     }
  164.   },
  165.  
  166.   /////////////////////////////////////////////////////////////////////////////
  167.   // nsISupports
  168.   QueryInterface: function (aIID) 
  169.   {
  170.     if (!aIID.equals(Components.interfaces.nsIExtensionManager) &&
  171.         !aIID.equals(Components.interfaces.nsIObserver) &&
  172.         !aIID.equals(Components.interfaces.nsISupports))
  173.       throw Components.results.NS_ERROR_NO_INTERFACE;
  174.     return this;
  175.   }
  176. };
  177.  
  178. function nsExtensionUpdater(aExtensionID, aExtensionVersion, 
  179.                             aTargetAppID, aTargetAppVersion) 
  180. {
  181.   this._extensionID = aExtensionID;
  182.   this._extensionVersion = aExtensionVersion;
  183.   this._appID = aTargetAppID;
  184.   this._appVersion = aTargetAppVersion;
  185. }
  186.  
  187. nsExtensionUpdater.prototype = {
  188.   /////////////////////////////////////////////////////////////////////////////
  189.   // 
  190.   checkForUpdates: function (aDoneUpdatingFunc) 
  191.   {
  192.     this._doneUpdatingFunc = aDoneUpdatingFunc;
  193.     
  194.     var wspFactory = Components.classes["@mozilla.org/xmlextras/proxy/webserviceproxyfactory;1"]
  195.                               .getService(Components.interfaces.nsIWebServiceProxyFactory);
  196.     wspFactory.createProxyAsync("http://localhost:8080/axis/services/VersionCheck?wsdl", 
  197.                                 "VersionCheck", "", true, this);
  198.   },
  199.  
  200.   _proxy: null,
  201.   
  202.   _getExtensionUpdateURL: function (aExtensionGUID, aInstalledVersion,
  203.                                     aTargetApp, aTargetAppVersion)
  204.   {
  205.     this._proxy.getNewestExtension(aExtensionGUID, aInstalledVersion, aTargetApp, aTargetAppVersion);
  206.   },
  207.  
  208.   /////////////////////////////////////////////////////////////////////////////
  209.   // nsIWSDLLoadListener  
  210.   onLoad: function (aProxy)
  211.   { 
  212.     this._proxy = aProxy;
  213.     this._proxy.setListener(this);
  214.     this._getExtensionUpdateURL(this._extensionID, this._extensionVersion, this._appID, this._appVersion);
  215.   },
  216.   
  217.   onError: function (aError)
  218.   {
  219.     dump("*** onError ERROR = " + aError + "\n");
  220.   },
  221.   
  222.   getNewestExtensionCallback: function (aResult)
  223.   {
  224.     this._newestID = aResult;
  225.     
  226.     if (this._newestID != -1)
  227.       this._proxy.getProperty(this._newestID, "xpiurl");
  228.   },
  229.  
  230.   getPropertyCallback: function (aResult)
  231.   {
  232.     this._doneUpdatingFunc(this._extensionID, aResult);
  233.   }
  234. };
  235.  
  236.  
  237. const PREF_EM_DEFAULTUPDATEURL = "update.url.extensions";
  238. const PREF_EM_APP_ID = "app.id";
  239. const PREF_EM_APP_VERSION = "general.useragent.vendorSub";
  240.  
  241. function EM_NS(aProperty)
  242. {
  243.   return "http://www.mozilla.org/2004/em-rdf#" + aProperty;
  244. }
  245.  
  246. function nsExtensionsDataSource()
  247. {
  248.   this._rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  249.                         .getService(Components.interfaces.nsIRDFService);
  250. }
  251.  
  252. nsExtensionsDataSource.prototype = {
  253.   _rdf: null,
  254.   
  255.   _emR: function (aProperty) 
  256.   {
  257.     return this._rdf.GetResource(EM_NS(aProperty));
  258.   },
  259.   
  260.   _emL: function (aLiteral)
  261.   {
  262.     return this._rdf.GetLiteral(aLiteral);
  263.   },
  264.   
  265.   _setProperty: function (aDS, aSource, aProperty, aNewValue)
  266.   {
  267.     var oldValue = aDS.GetTarget(aSource, aProperty, true);
  268.     if (oldValue)
  269.       aDS.Change(aSource, aProperty, oldValue, aNewValue);
  270.     else
  271.       aDS.Assert(aSource, aProperty, aNewValue, true);
  272.   },
  273.   
  274.   getExtensionProperty: function (aExtensionID, aProperty)
  275.   {
  276.     var extension = this._rdf.GetResource("urn:mozilla:extension:" + aExtensionID);
  277.     return this.GetTarget(extension, this._emR(aProperty), true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  278.   },
  279.   
  280.   _setExtensionProperty: function (aExtensionID, aPropertyArc, aPropertyValue)
  281.   {
  282.     var extension = this._rdf.GetResource("urn:mozilla:extension:" + aExtensionID);
  283.     var installLocation = this.GetTarget(extension, this._emR("installLocation"), true);
  284.     var isProfile = installLocation.EqualsNode(this._emR("profile"));
  285.     var ds = isProfile ? this._profileExtensions : this._appExtensions;
  286.     
  287.     this._setProperty(ds, extension, aPropertyArc, aPropertyValue);
  288.  
  289.     this._flush(isProfile);  
  290.   },
  291.   
  292.   enableExtension: function (aExtensionID)
  293.   {
  294.     this._setExtensionProperty(aExtensionID, this._emR("toBeEnabled"), this._emL("true"));
  295.     this._setExtensionProperty(aExtensionID, this._emR("toBeDisabled"), this._emL("false"));
  296.     this._setExtensionProperty(aExtensionID, this._emR("disabled"), this._emL("false"));
  297.   },
  298.   
  299.   disableExtension: function (aExtensionID)
  300.   {
  301.     this._setExtensionProperty(aExtensionID, this._emR("toBeDisabled"), this._emL("true"));
  302.     this._setExtensionProperty(aExtensionID, this._emR("toBeEnabled"), this._emL("false"));
  303.     this._setExtensionProperty(aExtensionID, this._emR("disabled"), this._emL("true"));
  304.   },
  305.   
  306.   uninstallExtension: function (aExtensionID)
  307.   {
  308.     var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  309.                         .createInstance(Components.interfaces.nsIRDFContainer);
  310.     ctr.Init(this, this._rdf.GetResource("urn:mozilla:extension:root"));
  311.     
  312.     var extension = this._rdf.GetResource("urn:mozilla:extension:" + aExtensionID);
  313.     ctr.RemoveElement(extension, true);
  314.     
  315.     this._setExtensionProperty(aExtensionID, this._emR("toBeUninstalled"), this._emL("true"));
  316.   },
  317.   
  318.   getUpdateURLs: function (aExtensionID)
  319.   {
  320.     var pref = Components.classes["@mozilla.org/preferences-service;1"]
  321.                          .getService(Components.interfaces.nsIPrefBranch);
  322.     var appID = pref.getCharPref(PREF_EM_APP_ID);
  323.   
  324.     var urls = [];
  325.     if (aExtensionID) {
  326.       var updateURL = this._getUpdateURLInternal(aExtensionID);
  327.       updateURL = updateURL.replace(/%APP%/g, escape(appID));
  328.       updateURL = updateURL.replace(/%ITEM%/g, escape(aExtensionID));
  329.       urls.push(updateURL);
  330.     }
  331.     else {
  332.       var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  333.                           .createInstance(Components.interfaces.nsIRDFContainer);
  334.       ctr.Init(this, this._rdf.GetResource("urn:mozilla:extension:root"));
  335.       
  336.       var urlHash = { };
  337.       
  338.       var e = ctr.GetElements();
  339.       while (e.hasMoreElements()) {
  340.         var r = e.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  341.         var extensionID = r.Value.substr("urn:mozilla:extension:".length, r.Value.length);
  342.         var updateURL = this._getUpdateURLInternal(extensionID);
  343.         if (!(updateURL in urlHash))
  344.           urlHash[updateURL] = [];
  345.           
  346.         urlHash[updateURL].push(extensionID);
  347.       }
  348.       
  349.       for (var url in urlHash) {
  350.         var guidString = "";
  351.         var urlCount = urlHash[url].length;
  352.         for (var i = 0; i < urlCount; ++i)
  353.           guidString += escape(urlHash[url][i] + (i < urlCount - 1 ? "," : ""));
  354.         url = url.replace(/%APP%/g, appID);
  355.         url = url.replace(/%ITEM%/g, guidString);
  356.         urls.push(url);
  357.       }
  358.     }
  359.     return urls;
  360.   },
  361.   
  362.   _getUpdateURLInternal: function (aExtensionID)
  363.   {
  364.     var updateURL;
  365.     var extension = this._rdf.GetResource("urn:mozilla:extension:" + aExtensionID);
  366.    
  367.     if (this.hasArcOut(extension, this._emR("updateURL"))) {
  368.       updateURL = this.GetTarget(extension, this._emR("updateURL"), true);
  369.       if (updateURL) 
  370.         updateURL = updateURL.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  371.     }
  372.     
  373.     if (!updateURL) {
  374.       var pref = Components.classes["@mozilla.org/preferences-service;1"]
  375.                            .getService(Components.interfaces.nsIPrefBranch);
  376.       updateURL = pref.getCharPref(PREF_EM_DEFAULTUPDATEURL);
  377.     }
  378.     return updateURL;
  379.   },
  380.   
  381.   loadExtensions: function (aProfile)
  382.   {
  383.     var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
  384.     var key = aProfile ? "ProfD" : "ProfD"; // XCurProcDir
  385.     var extensionsFile = fileLocator.get(key, Components.interfaces.nsIFile);
  386.     extensionsFile.append("Extensions");
  387.     extensionsFile.append("extensions.rdf");
  388.     
  389.     if (!extensionsFile.exists())
  390.       return;
  391.  
  392.     var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
  393.                            .getService(Components.interfaces.nsIIOService);
  394.     var fph = ioServ.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  395.     var dsURL = fph.getURLSpecFromFile(extensionsFile);
  396.     
  397.     var ds = this._rdf.GetDataSourceBlocking(dsURL);
  398.     
  399.     if (aProfile) {
  400.       this._profileExtensions = ds;
  401.       if (!this._composite) 
  402.         this._composite = Components.classes["@mozilla.org/rdf/datasource;1?name=composite-datasource"]
  403.                                     .createInstance(Components.interfaces.nsIRDFDataSource);
  404.       if (this._appExtensions)
  405.         this._composite.RemoveDataSource(this._appExtensions);
  406.       this._composite.AddDataSource(this._profileExtensions);
  407.       if (this._appExtensions)
  408.         this._composite.AddDataSource(this._appExtensions);  
  409.     }
  410.     else {
  411.       this._appExtensions = ds;
  412.       
  413.       if (!this._composite)
  414.         this._composite = Components.classes["@mozilla.org/rdf/datasource;1?name=composite-datasource"]
  415.                                     .createInstance(Components.interfaces.nsIRDFCompositeDataSource);
  416.       this._composite.AddDataSource(this._appExtensions);
  417.     }
  418.   },
  419.   
  420.   /////////////////////////////////////////////////////////////////////////////
  421.   // nsIRDFDataSource
  422.  
  423.   _appExtensions: null,
  424.   _profileExtensions: null,  
  425.   _composite: null,
  426.   
  427.   get URI()
  428.   {
  429.     return "rdf:extensions";
  430.   },
  431.   
  432.   GetSource: function (aProperty, aTarget, aTruthValue)
  433.   {
  434.     return this._composite.GetSource(aProperty, aTarget, aTruthValue);
  435.   },
  436.   
  437.   GetSources: function (aProperty, aTarget, aTruthValue)
  438.   {
  439.     return this._composite.GetSources(aProperty, aTarget, aTruthValue);
  440.   },
  441.   
  442.   GetTarget: function (aSource, aProperty, aTruthValue)
  443.   {
  444.     if (aProperty.EqualsNode(this._emR("iconURL"))) {
  445.       var hasIconURL = this._composite.hasArcOut(aSource, aProperty);
  446.       // If the download entry doesn't have a IconURL property, use a
  447.       // generic icon URL instead.
  448.       if (!hasIconURL)
  449.         return this._rdf.GetResource("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png");
  450.     }
  451.     else if (aProperty.EqualsNode(this._emR("installLocation"))) {
  452.       var hasNameArc = this._profileExtensions.hasArcOut(aSource, this._emR("name"));
  453.       var hasVersionArc = this._profileExtensions.hasArcOut(aSource, this._emR("version"));
  454.       return hasNameArc && hasVersionArc ? this._emL("profile") : this._emL("global");
  455.     }
  456.     
  457.     return this._composite.GetTarget(aSource, aProperty, aTruthValue);
  458.   },
  459.   
  460.   GetTargets: function (aSource, aProperty, aTruthValue)
  461.   {
  462.     return this._composite.GetTargets(aSource, aProperty, aTruthValue);
  463.   },
  464.   
  465.   Assert: function (aSource, aProperty, aTarget, aTruthValue)
  466.   {
  467.     return Components.results.NS_RDF_ASSERTION_REJECTED;
  468.   },
  469.   
  470.   Unassert: function (aSource, aProperty, aTarget)
  471.   {
  472.     return Components.results.NS_RDF_ASSERTION_REJECTED;
  473.   },
  474.   
  475.   Change: function (aSource, aProperty, aOldTarget, aNewTarget)
  476.   {
  477.     return Components.results.NS_RDF_ASSERTION_REJECTED;
  478.   },
  479.  
  480.   Move: function (aSource, aNewSource, aProperty, aTarget)
  481.   {
  482.     return Components.results.NS_RDF_ASSERTION_REJECTED;
  483.   },
  484.   
  485.   HasAssertion: function (aSource, aProperty, aTarget, aTruthValue)
  486.   {
  487.     return this._composite.HasAssertion(aSource, aProperty, aTarget, aTruthValue);
  488.   },
  489.   
  490.   AddObserver: function (aObserver)
  491.   {
  492.     this._composite.AddObserver(aObserver);
  493.   },
  494.   
  495.   RemoveObserver: function (aObserver)
  496.   {
  497.     this._composite.RemoveObserver(aObserver);
  498.   },
  499.   
  500.   ArcLabelsIn: function (aNode)
  501.   {
  502.     return this._composite.ArcLabelsIn(aNode);
  503.   },
  504.   
  505.   ArcLabelsOut: function (aSource)
  506.   {
  507.     return this._composite.ArcLabelsOut(aSource);
  508.   },
  509.   
  510.   GetAllResources: function ()
  511.   {
  512.     return this._composite.GetAllResources();
  513.   },
  514.   
  515.   IsCommandEnabled: function (aSources, aCommand, aArguments)
  516.   {
  517.     return this._composite.IsCommandEnabled(aSources, aCommand, aArguments);
  518.   },
  519.   
  520.   DoCommand: function (aSources, aCommand, aArguments)
  521.   {
  522.     this._composite.DoCommand(aSources, aCommand, aArguments);
  523.   },
  524.   
  525.   GetAllCmds: function (aSource)
  526.   {
  527.     return this._composite.GetAllCmds(aSource);
  528.   },
  529.   
  530.   hasArcIn: function (aNode, aArc)
  531.   {
  532.     return this._composite.hasArcIn(aNode, aArc);
  533.   },
  534.   
  535.   hasArcOut: function (aSource, aArc)
  536.   {
  537.     return this._composite.hasArcOut(aSource, aArc);
  538.   },
  539.   
  540.   beginUpdateBatch: function ()
  541.   {
  542.     return this._composite.beginUpdateBatch();
  543.   },
  544.   
  545.   endUpdateBatch: function ()
  546.   {
  547.     return this._composite.endUpdateBatch();
  548.   },
  549.   
  550.   /////////////////////////////////////////////////////////////////////////////
  551.   // nsIRDFRemoteDataSource
  552.   
  553.   get loaded()
  554.   {
  555.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  556.   },
  557.   
  558.   init: function (aURI)
  559.   {
  560.   },
  561.   
  562.   refresh: function (aBlocking)
  563.   {
  564.   },
  565.   
  566.   flush: function ()
  567.   {
  568.     this._flush(false);
  569.     this._flush(true);
  570.   },
  571.   
  572.   flushTo: function (aURI)
  573.   {
  574.   },
  575.   
  576.   _flush: function (aIsProfile)
  577.   { 
  578.     var ds = aIsProfile ? this._profileExtensions : this._appExtensions;
  579.     var rds = ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  580.     rds.Flush();
  581.   },
  582.  
  583.   /////////////////////////////////////////////////////////////////////////////
  584.   // nsISupports
  585.   QueryInterface: function (aIID) 
  586.   {
  587.     if (!aIID.equals(Components.interfaces.nsIRDFDataSource) &&
  588.         !aIID.equals(Components.interfaces.nsIRDFRemoteDataSource) && 
  589.         !aIID.equals(Components.interfaces.nsISupports))
  590.       throw Components.results.NS_ERROR_NO_INTERFACE;
  591.     return this;
  592.   }
  593. };
  594.  
  595.  
  596. var gModule = {
  597.   _firstTime: true,
  598.   
  599.   registerSelf: function (aComponentManager, aFileSpec, aLocation, aType) 
  600.   {
  601.     if (this._firstTime) {
  602.       this._firstTime = false;
  603.       throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  604.     }
  605.     aComponentManager = aComponentManager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  606.     
  607.     aComponentManager.registerFactoryLocation(this._cid, 
  608.                                               "Extension Manager",
  609.                                               this._contractId,
  610.                                               aFileSpec,
  611.                                               aLocation,
  612.                                               aType);
  613.     
  614.     // Make the Extension Manager a startup observer
  615.     var categoryManager = Components.classes["@mozilla.org/categorymanager;1"]
  616.                                     .getService(Components.interfaces.nsICategoryManager);
  617.     categoryManager.addCategoryEntry("app-startup", "Extension Manager", 
  618.                                      "service," + this._contractId, true, true, null);
  619.   },
  620.   
  621.   getClassObject: function (aComponentManager, aCID, aIID) 
  622.   {
  623.     if (!aCID.equals(this._cid))
  624.       throw Components.results.NS_ERROR_NO_INTERFACE;
  625.       
  626.     if (!aIID.equals(Components.interfaces.nsIFactory))
  627.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  628.     
  629.     return this._factory;
  630.   },
  631.  
  632.   _cid: Components.ID("{8A115FAA-7DCB-4e8f-979B-5F53472F51CF}"),
  633.  
  634.   _contractId: "@mozilla.org/extension-manager;1",
  635.   
  636.   _factory: {
  637.     createInstance: function (aOuter, aIID) 
  638.     {
  639.       if (aOuter != null)
  640.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  641.       
  642.       if (!gExtensionManager)
  643.         gExtensionManager = new nsExtensionManager();
  644.         
  645.       return gExtensionManager.QueryInterface(aIID);
  646.     }
  647.   },
  648.   
  649.   canUnload: function (aComponentManager) 
  650.   {
  651.     return true;
  652.   }
  653. };
  654.  
  655. function NSGetModule(compMgr, fileSpec) 
  656. {
  657.   return gModule;
  658. }
  659.